import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.apache.commons.math3.linear.DecompositionSolver;
import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;



public class SupplyForecast {
	//state params to associate wind speeds
	//private static double windStateSize = 5;
	//private static int numWindStates = 10;
	private static enum Mode{MidPointOfMostLikely,PartialSumOfProbabilities}
	static Mode powerOutputAndStatePredictionCalculationMethod = Mode.PartialSumOfProbabilities;
	
	private static final String TRAINING_WINDFORECAST="harbec_data_forecast_row_april_may2012_75percent.csv";
	private static final String TRAINING_POWEROUTPUT="harbec_data_output_row_april_may_2012_75percent.csv";
	
	private static final String TESTDATA_WINDFORECAST="harbec_data_forecast_row_april_may2012_25percent.csv";
	private static final String TESTDATA_POWEROUTPUT="harbec_data_output_row_april_may_2012_25percent.csv";
	
	private static final double MAX_WIND=50;//default 50
	private static int windResolutionMin=2;//default 2
	private static int windResolutionMax=2;//default 50
	private static int windResolutionIncrement=1;//default 1
	
	//state params to associate power outputs
	private static int numPowerStates = 300;//default 20
	private static int powerStateSize = 1;//default 15

	
	private static final double MAX_POWER= 300;//default 300
	//which state of wind should be considered
	//private static int wind_state = 0;
	private static int SLEEP_TIME = 10;//default 10
	private static boolean debug = false;//default false
	private static boolean printHitMaps = false;//default false
	private static boolean testDataIterations = false;//default false
	
	//class variables
	static TreeMap<String, TreeMap<Integer,Double[]>> steadyStateProbabilityMap;

	public static void main(String[] args) throws IOException, InterruptedException {
		DecimalFormat df = new DecimalFormat("#.000");


		//Create a map to hold results
		TreeMap<String,Double> resultMap10Mins = new TreeMap<String, Double>(new NumbersThenWordsComparator());
		
		//Create a map to hold results
		TreeMap<String,Double> resultMapInf = new TreeMap<String, Double>(new NumbersThenWordsComparator());
		
		//create a map to hold relative error
		TreeMap<String,Double[]> errorMapInf = new TreeMap<String, Double[]>(new NumbersThenWordsComparator());//first double -LB, second double -UB, third double Avg
		for(int res= windResolutionMin;res<=windResolutionMax;res = res+ windResolutionIncrement){
			Double[] temp ={0.0,0.0,0.0};
			errorMapInf.put(res+"", temp);
		}
		//initialize steady state map
		steadyStateProbabilityMap = new TreeMap<String,TreeMap<Integer,Double[]>>(new NumbersThenWordsComparator());
		for(int res= windResolutionMin;res<=windResolutionMax;res = res+ windResolutionIncrement){
			steadyStateProbabilityMap.put(res+"", new TreeMap<Integer,Double[]>());
		}
		//Create wind state resolution map
		TreeMap<String, List<TreeMap<String,TreeMap<String,Double>>>> windResolutionMap = new TreeMap<String, List<TreeMap<String,TreeMap<String,Double>>>>(new NumbersThenWordsComparator());
		for(int res= windResolutionMin;res<=windResolutionMax;res = res+ windResolutionIncrement){
			int numStates = (int) (MAX_WIND/res);
			windResolutionMap.put(res+"", createPowerStateTransitionMapList(res,numStates));
		}
		//print steady states
		System.out.println("Steady state probabilities");
		for(Entry<String,TreeMap<Integer,Double[]>> windResEntry: steadyStateProbabilityMap.entrySet()){
			
			System.out.println("Steady State probablilites for resolution: "+ windResEntry.getKey() +"mph");
			System.out.print("WindSt\\PowerSt\t");
			for(int q=0;q<numPowerStates;q++){
				System.out.print("S"+q+"\t");
			}
			System.out.println();
			for(Entry<Integer,Double[]> steadyStateEntry: windResEntry.getValue().entrySet()){
				
				System.out.print(getWindStateLowerBound(steadyStateEntry.getKey(),Integer.parseInt(windResEntry.getKey()))+"-"+getWindStateUpperBound(steadyStateEntry.getKey(),Integer.parseInt(windResEntry.getKey()))+"mph\t");
				Double[] steadyStateValues= steadyStateEntry.getValue();
				for(double p:steadyStateValues){
					System.out.print(df.format(p)+"\t");
				}
				System.out.println();
			}	
			System.out.println("------------x---------");
		}
		//Load test data
		DataPointManager dpm = new DataPointManager(TESTDATA_POWEROUTPUT,TESTDATA_WINDFORECAST);
		//Get all the test datapoints(hourly)
		TreeMap<Date, DataPoint> dpMap = dpm.getDataPoints();
		//Convert map to list so that previous values and next values can be naturally obtained instead of making a lookup call
		List<DataPoint> dpList = new ArrayList<DataPoint>(dpMap.values());

//		for (int d=0; d<dpList.size();d=d+8){//DataPoint dp : dpList) {
//			DataPoint dp = dpList.get(d);
//			System.out.println(dp.getId() + " " + dp.getForecastedDateTime() + " " + dp.getForecastDate() + " "
//					+ dp.getWindSpeed()+ " "+df.format(dp.getAvgPowerOutput())+ " "+df.format(dp.getMedianPowerOutput()));
//		}

		for(Integer res= windResolutionMin;res<=windResolutionMax;res=res+windResolutionIncrement){
			int numStates = (int) (MAX_WIND/res);
			List<TreeMap<String,TreeMap<String,Double>>> powerStateTransitionMapList = windResolutionMap.get(res+"");
			
if(testDataIterations)			System.out.println("Resolution\tCur_wind_spd\tCur_pwr_state\tPred_nxt_pwr_state\tProb_prediction\tActual_nxt_pwr_state");
			int predictionCounter10Mins=0;
			int predictionCounterInf=0;
			ArrayList<Double> relativeErrorLB = new ArrayList<Double>();
			ArrayList<Double> relativeErrorUB = new ArrayList<Double>();
			ArrayList<Double> relativeErrorAvg = new ArrayList<Double>();
			ArrayList<Double> actualOutputList = new ArrayList<Double>();
			ArrayList<Double> calculatedAvgOutputList = new ArrayList<Double>();
			ArrayList<Double> calculatedAbsErrorList = new ArrayList<Double>();
			System.err.println("Time\t\t\tWindSpd\tPredictedkw\tMeasuredkw");
			for (int d=0; d<dpList.size()-1;d++){//DataPoint dp : dpList) {
				DataPoint dp = dpList.get(d);
				DataPoint nextDp = dpList.get(d+1);
				
				int currentWindState = (int) (dp.getWindSpeed()/res);
				
				TreeMap<String,TreeMap<String,Double>> correspondingPowerStateTransitionMap = powerStateTransitionMapList.get(currentWindState);
				double currentPower = dp.getAvgPowerOutput();
				double nextPower = nextDp.getAvgPowerOutput();//10mins
				
				int currentPowerState = (int)(currentPower/powerStateSize);
				int nextPowerState =(int)(nextPower/powerStateSize);//10mins
				

				//get "To" Map
				TreeMap<String,Double> toMap = correspondingPowerStateTransitionMap.get("S"+currentPowerState);
				Entry<String,Double> maxEntry = null;

				//find the highest probability of "to" state for the currentPower/"from" state
				for(Entry<String,Double> entry : toMap.entrySet()) {
				    if (maxEntry == null || entry.getValue() > maxEntry.getValue()) {
				        maxEntry = entry;
				    }
				}
				
				//update prediction counter for next state 10 minutes version, using power as the input
				int predictedNextState10mins = Integer.parseInt(maxEntry.getKey().substring(1));
				
				double predictionProbability10mins = maxEntry.getValue();
				if(nextPowerState==predictedNextState10mins){//10mins
					predictionCounter10Mins++;
				}
				
				
				//update prediction counter for next state using wind as the input, at any given time
				TreeMap<Integer, Double[]> steadyStateMap_specific_resolution= steadyStateProbabilityMap.get(res+"");

				Double[] steadyStateValues=steadyStateMap_specific_resolution.get(currentWindState);
				double calculatedOutput=0.0;

				int predictedNextStateIndex_or_maxValueIndex=0;
				double maxValue=0;
				if(powerOutputAndStatePredictionCalculationMethod.equals(Mode.PartialSumOfProbabilities)){
			//	System.err.print("predicted output= ");
				for(int ssI=0;ssI<steadyStateValues.length;ssI++){
					if(steadyStateValues[ssI]>maxValue){
						maxValue=steadyStateValues[ssI];
						predictedNextStateIndex_or_maxValueIndex=ssI;
					}
			//		System.err.print(df.format(steadyStateValues[ssI])+"x"+df.format(getPowerStateAvg(ssI, powerStateSize)) +" + ");
					calculatedOutput=calculatedOutput+steadyStateValues[ssI]*getPowerStateAvg(ssI, powerStateSize);
					
				}
				} else if(powerOutputAndStatePredictionCalculationMethod.equals(Mode.MidPointOfMostLikely)){
					calculatedOutput=getPowerStateAvg(predictedNextStateIndex_or_maxValueIndex, powerStateSize);
				}
				
				//System.err.println();
				//System.err.println(df.format(calculatedOutput)+"kw belongs to state "+ getPowerState(calculatedOutput,powerStateSize) + "(To measure accuracy)");
				int predictedNextStateInf=-999;
				if(powerOutputAndStatePredictionCalculationMethod.equals(Mode.MidPointOfMostLikely)){
					predictedNextStateInf=predictedNextStateIndex_or_maxValueIndex; //most likely state
				}else if(powerOutputAndStatePredictionCalculationMethod.equals(Mode.PartialSumOfProbabilities) ){
					//Sum of probabilities
					predictedNextStateInf = getPowerState(calculatedOutput,powerStateSize);
				}
				if(nextPowerState==predictedNextStateInf){
					predictionCounterInf++;
				}
				
				//calculate relative error and store it in array
				//calculate re for lower bound
				//System.err.println("LB calc: "+nextDp.getAvgPowerOutput()+"\t"+getPowerStateLowerBound(nextPowerState, powerStateSize));
				double re_lb=Math.abs(nextDp.getAvgPowerOutput()-getPowerStateLowerBound(predictedNextStateInf, powerStateSize))/nextDp.getAvgPowerOutput();
				relativeErrorLB.add(re_lb);
				//System.err.print("Putting into list " +re_lb+"\t");
				//calculate re for upper bound
				double re_ub=Math.abs(nextDp.getAvgPowerOutput()-getPowerStateUpperBound(predictedNextStateInf, powerStateSize))/nextDp.getAvgPowerOutput();
				relativeErrorUB.add(re_ub);
				//System.err.print(re_ub+"\t");
				//calculate re for avg
				double re_avg=Math.abs(nextDp.getAvgPowerOutput()-getPowerStateAvg(predictedNextStateInf, powerStateSize))/nextDp.getAvgPowerOutput();
				relativeErrorAvg.add(re_avg);
				//System.err.println(re_avg+"\t");
				
				actualOutputList.add(nextDp.getAvgPowerOutput());

				//most likely state
				//calculatedAvgOutputList.add(getPowerStateAvg(predictedNextStateInf, powerStateSize));
				//sum of probabilities
				calculatedAvgOutputList.add(calculatedOutput);
				
				double absError=999999.00;
				if(powerOutputAndStatePredictionCalculationMethod.equals(Mode.MidPointOfMostLikely)){
				//most likey state
					absError= Math.abs(nextDp.getAvgPowerOutput()-getPowerStateAvg(predictedNextStateInf, powerStateSize));
				}else if(powerOutputAndStatePredictionCalculationMethod.equals(Mode.PartialSumOfProbabilities) ){
				//sum of probabilities
					absError= Math.abs(nextDp.getAvgPowerOutput()-calculatedOutput);
				}
				calculatedAbsErrorList.add(absError);
				
				//System.err.println(res+"\t"+nextPowerState+"\t"+nextDp.getAvgPowerOutput()+"\t"+getPowerStateAvg(predictedNextStateInf, powerStateSize)+"\t"+getPowerStateAvg(nextPowerState, powerStateSize));
				
				//Print out prediction checks for 10 minute version
if(testDataIterations)				System.out.println( res+"\t"+ dp.getWindSpeed()+"\tS"+ currentPowerState +"\tS"+predictedNextState10mins+"\t"+df.format(predictionProbability10mins)+"\tS"+nextPowerState);
			
				System.err.println(nextDp.getForecastedDateTime()+"\t"+nextDp.getWindSpeed()+"\t"+df.format(calculatedOutput)+"\t"+df.format(nextDp.getAvgPowerOutput()));
			
			}
			//average relative error of each datapoint
			double re_lb_avg=getAvgOfCollection(relativeErrorLB);//unused
			double re_ub_avg=getAvgOfCollection(relativeErrorUB);//unused
			double re_avg_avg=getAvgOfCollection(relativeErrorAvg);//unused
			
			double avg_rel_error_avg = (getAvgOfCollection(actualOutputList)-getAvgOfCollection(calculatedAvgOutputList))/getAvgOfCollection(actualOutputList);
			double avg_abs_error=getAvgOfCollection(calculatedAbsErrorList);
			Double[] errors={re_lb_avg,re_ub_avg,re_avg_avg,avg_rel_error_avg,avg_abs_error};

			errorMapInf.put(res+"", errors);
			//store accuracy into maps to print later
			resultMap10Mins.put(res+"", ((double)predictionCounter10Mins/dpList.size())*100);
			resultMapInf.put(res+"", ((double)predictionCounterInf/dpList.size())*100);
		}

		//Print out size of each map
		for(String key :windResolutionMap.keySet()){
			System.out.println( "Resolution: "+ key+"mph, "+ "Num Power State Transition Maps:"+windResolutionMap.get(key).size());
		}
		//Print the results 10 mins
//		System.out.println("Resolution(mph)\t %Accuracy (Next 10 minutes)");
//		//System.err.println(resultMap10Mins.entrySet().size());
//		for(Entry<String,Double> entry : resultMap10Mins.entrySet()) {
//			System.out.println(entry.getKey()+"\t\t"+df.format(entry.getValue()));
//		}
//		
		//Print the results Long term
		System.out.println("WindResolution(mph)\t %Accuracy (Steady State)\t AvgOfAbsoluteErrors ");
		for(Entry<String,Double> entry : resultMapInf.entrySet()) {
			Double[] relativeErrorInfo = errorMapInf.get(entry.getKey()); 
			System.out.println(entry.getKey()+"\t\t"+df.format(entry.getValue())+"\t\t\t\t\t"+df.format(relativeErrorInfo[4]));
		}

	}
	
	public static List<TreeMap<String, TreeMap<String, Double>>> createPowerStateTransitionMapList(int res, int numStates) throws InterruptedException, IOException{
		List<TreeMap<String, TreeMap<String, Double>>> powerStateTransitionMapList = new ArrayList<TreeMap<String, TreeMap<String, Double>>>();
		for(int state=0; state<numStates;state++){
			
			powerStateTransitionMapList.add(createPowerStateTransitionMap(res,state));
		}
		return powerStateTransitionMapList;
	}
	public static TreeMap<String, TreeMap<String, Double>> createPowerStateTransitionMap(int windStateRes, int windState) throws InterruptedException, IOException{
		//create a matrix to hold steady state equations.
		double[][] preSteadyStateMatrix = new double[numPowerStates][numPowerStates];
		
		// For a given state, ex:15-20mph
		// calculate markhov probabilities - probabilities of power states going into 15-20mph's power states
											//and probabilities of power states  going out of 15-20mph's power states

		//Initialize datapoint manager - holds datapoints and provides tools to work with datapoints
		DataPointManager dpm = new DataPointManager(TRAINING_POWEROUTPUT,
				TRAINING_WINDFORECAST);
		//Get all the datapoints(hourly)
		TreeMap<Date, DataPoint> dpMap = dpm.getDataPoints();
		//Convert map to list so that previous values and next values can be naturally obtained instead of making a lookup call
		List<DataPoint> dpList = new ArrayList<DataPoint>(dpMap.values());

		//print out all the datapoints
		DecimalFormat df = new DecimalFormat("#.000");
if(debug){
		for (int d=0; d<dpList.size();d=d+8){//DataPoint dp : dpList) {
			DataPoint dp = dpList.get(d);
			System.out.println(dp.getId() + " " + dp.getForecastedDateTime() + " " + dp.getForecastDate() + " "
					+ dp.getWindSpeed()+ " "+df.format(dp.getAvgPowerOutput())+ " "+df.format(dp.getMedianPowerOutput()));
		}
}
		// Declare containers to hold markhov results
		TreeMap<String, TreeMap<String, Integer>> markovHitMap = new TreeMap<String, TreeMap<String, Integer>>(
				new NumbersThenWordsComparator());
		TreeMap<String, TreeMap<String, Double>> markovProbMap = new TreeMap<String, TreeMap<String, Double>>(
				new NumbersThenWordsComparator());
		// Initialize containers
		for (int i = 0; i < numPowerStates; i++) {
			markovHitMap.put("S" + i, new TreeMap<String, Integer>(new NumbersThenWordsComparator()));
			markovProbMap.put("S" + i, new TreeMap<String, Double>(new NumbersThenWordsComparator()));
			for (int j = 0; j < numPowerStates; j++) {
				markovHitMap.get("S" + i).put("S" + j, 0);
				markovProbMap.get("S" + i).put("S" + j, 0.0);
			}
		}
		double previousOutput = 0;
		double currentOutput = 0;
		boolean collectNext = false;
		for (DataPoint dp : dpList) {
			currentOutput = dp.getAvgPowerOutput();
			int current_output_belongs_to_state = (int) (currentOutput / powerStateSize);
			int previous_output_belongs_to_state = (int) (previousOutput / powerStateSize);

			// check if wind speed is in the correct state, otherwise dont do
			// next block. previousOutput lets us see the transition from previous state into current state
			// Use a boolean such that, if you current state is valid, you also read the next state to
			// to see the transitions from current state to the next state

			// if wind speed correct state or boolean collect next is true

			if (getWindState(dp.getWindSpeed(),windStateRes) == windState || collectNext == true) {
				// set collact next to false (default)
				collectNext = false;
				int current_state_transition_Count = markovHitMap.get("S" + previous_output_belongs_to_state).get(
						"S" + current_output_belongs_to_state);
				int updated_current_state_transition_Count = current_state_transition_Count + 1;
				markovHitMap.get("S" + previous_output_belongs_to_state).put("S" + current_output_belongs_to_state,
						updated_current_state_transition_Count);
				// if windspeed in the evaluation state, set collect next to true, to account transitions to next state
				if (getWindState(dp.getWindSpeed(),windStateRes) == windState) {
					collectNext = true;
				}
			}
			previousOutput = currentOutput;
		}
		System.out.println();
		System.out.println("Markov Table for All Power state transitions in the Wind State " + windState + " ("
				+ getWindStateLowerBound(windState,windStateRes) + " - " + getWindStateUpperBound(windState,windStateRes) + "mph )");
if(printHitMaps){
		// Print column labels
		for (String colLabel : markovHitMap.keySet()) {
			System.out.print("\t" + colLabel);
		}
		// print state values
		System.out.println();
		for (int i = 0; i < markovHitMap.size(); i++) {
			System.out.print("\t" + /* i*state_size+ */"->" + ((i + 1) * powerStateSize));
		}
		// print column labels
		System.out.println("\n");
		for (String colLabel : markovHitMap.keySet()) {
			System.out.print("\t" + colLabel);
		}
		// print the data in the container
		System.out.println();
		for (String rowLabel : markovHitMap.keySet()) {
			System.out.print(rowLabel + "\t");
			TreeMap<String, Integer> rowMap = markovHitMap.get(rowLabel);
			for (String col : rowMap.keySet()) {
				System.out.print(rowMap.get(col) + "\t");
			}
			System.out.println();

		}
		System.out.println();
}
		System.out.println("Probability Matrix for the Markov Table above");
		// Print column labels
		for (String colLabel : markovHitMap.keySet()) {
			System.out.print("\t" + colLabel);
		}
		System.out.println();
		// Calculate probabilities and print them
		//DecimalFormat df = new DecimalFormat("#.000");
		double mapSum =0;
		for(String row : markovHitMap.keySet()){
			TreeMap<String,Integer> rowMap = markovHitMap.get(row);
			for (String col:rowMap.keySet()){
				mapSum=mapSum+rowMap.get(col);
			}
		}
		int rowCounter=0;
		for (String row : markovHitMap.keySet()) {
			System.out.print(row + "\t");
			TreeMap<String, Integer> rowMap = markovHitMap.get(row);
			int rowSum = 0;
			for (int val : rowMap.values()) {
				rowSum = rowSum + val;
			}
			//double rowProbabilitySum=0;
			int colCounter=0;
			for (String col : rowMap.keySet()) {
				double prob=0;
				if(rowSum>1e-16){
					prob = (rowMap.get(col) / ((double) rowSum));
				}
				markovProbMap.get(row).put(col, prob);
				if(prob>1e-16){
					System.out.print(df.format(prob) + "\t");
				} else {
					System.out.print("x\t");
				}
				//rowProbabilitySum +=prob;
				preSteadyStateMatrix[rowCounter][colCounter]=prob;
				colCounter++;
			}
			//preSteadyStateMatrix[rowCounter][colCounter]=rowProbabilitySum;
			System.out.println(";");

			rowCounter++;
		}
		System.out.println("--------------------------------------------------------------------------\n--------------------------------------------------------------------------\n--------------------------------------------------------------------------\n");
		RealMatrix coefficients =   new Array2DRowRealMatrix(preSteadyStateMatrix,true);
		coefficients=coefficients.power(1000);
		
		double[] steadyStateProbabilities = coefficients.getRow(0);
		Double[] steadyStateProbabilities_Double = new Double[steadyStateProbabilities.length];
		int d = 0;
		for (double value : steadyStateProbabilities) {
			steadyStateProbabilities_Double[d++] = Double.valueOf(value);
		}

		steadyStateProbabilityMap.get(windStateRes+"").put(windState, steadyStateProbabilities_Double);
		System.out.println("Steady state probabilities for the above probability matrix");
		for(double p:steadyStateProbabilities){
			System.out.print(df.format(p)+"\t");
		}
		System.out.println();
		
		//calculate steady state probabilities
//        Matrix x = new Matrix(steadyStateMatrix);
//        System.out.println("before\n" + x.toString() + "\n");
//        x.RREF();
//        System.out.println("after\n" + x.toString() + "\n");
		
		
		Thread.sleep(SLEEP_TIME);
		return markovProbMap;
	}
	/**
	 * calculate which state a windspeed belongs to and return it
	 * @param windSpeed
	 * @return state association of a certain wind speed
	 */
	public static int getWindState(double windSpeed, int windStateResolution) {
		if (windSpeed / windStateResolution > MAX_WIND/windStateResolution) {
			System.err.println("Not Enough States: windSpeed:" + windSpeed + " Number of States: " + MAX_WIND/windStateResolution
					+ " State Size: " + windStateResolution);
			System.exit(-1);
		}
		return (int) (windSpeed / windStateResolution);
	}
	public static int getPowerState(double power, int powerResolution) {
		if (power / powerResolution > MAX_POWER/powerResolution) {
			System.err.println("Not Enough States: power:" + power + "kw Number of States: " + MAX_POWER/powerResolution
					+ " State Size: " + powerResolution);
			System.exit(-1);
		}
		return (int) (power / powerResolution);
	}
	/**
	 * 
	 * @param windState
	 * @return the lower bound of given wind state
	 */
	public static double getWindStateLowerBound(int windState, int windStateResolution) {
		return windState * windStateResolution;
	}
	public static double getPowerStateLowerBound(int powerState, int powerStateResolution) {
		return powerState * powerStateResolution;
	}
	/**
	 * 
	 * @param windState
	 * @return the upper bound of given wind state
	 */
	public static double getWindStateUpperBound(int windState, int windStateResolution) {
		return (windState + 1) * windStateResolution;
	}
	public static double getPowerStateUpperBound(int powerState, int powerStateResolution) {
		return (powerState + 1) * powerStateResolution;
	}
	
	public static double getPowerStateAvg(int powerState, int powerStateResolution) {
		return (getWindStateLowerBound(powerState,powerStateResolution)+getWindStateUpperBound(powerState,powerStateResolution))/2.0;
	}
	public static double getAvgOfCollection(Collection<Double> c) {

		
		double total = 0.0;
		for (double partial : c) {
			total += partial;
		}
		if (c.size() != 0) {
			return total / c.size();
		} else {
			return 0.0;
		}
	}
	/**
	 * 
	 * @return all the possible list of wind states
	 */
//	public static int[] getWindStates() {
//		int[] temp = new int[numWindStates];
//		for (int i = 0; i < numWindStates; i++) {
//			temp[i] = i;
//		}
//		return temp;
//	}
}
